/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package image.processing;

import image.histogram.Histogram;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;


/**
 * 
 * @author tommy
 */
public class ImageProcessor {

	public static enum CompressAlgorithm {
		LINEAR, DYNAMIC, JAVADEFAULT
	};

	public static BufferedImage add(BufferedImage img1, BufferedImage img2,
			CompressAlgorithm algorithm) {
		BufferedImage ans;
		int color;

		if (img1 == null) {
			return img2;
		} else if (img2 == null) {
			return img1;
		} else if (img1.getWidth() != img2.getWidth()
				|| img1.getHeight() != img2.getHeight()) {
			return null;
		} else {
			if (img1.getType() == BufferedImage.TYPE_BYTE_GRAY
					&& img2.getType() == BufferedImage.TYPE_BYTE_GRAY) {

				ans = new BufferedImage(img1.getWidth(), img1.getHeight(),
						BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();
				int intValues[][] = new int[img1.getWidth()][img1.getHeight()];
				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {
						int pixelSumado = img1.getRaster().getSample(x, y, 0)
								+ img2.getRaster().getSample(x, y, 0);
						intValues[x][y] = pixelSumado;

					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues = DynamicRangeCompressionChannel(intValues);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues = LinearRangeCompressionChannel(intValues);
				}
				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues[x][y]);
					}
				}
			} else if (img1.getType() == BufferedImage.TYPE_3BYTE_BGR && 
				img2.getType() == BufferedImage.TYPE_3BYTE_BGR) {
				ans = new BufferedImage(img1.getWidth(), img1.getHeight(),
						BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				WritableRaster originalRaster1 = img1.getRaster();
				WritableRaster originalRaster2 = img2.getRaster();
				int intValues0[][] = new int[img1.getWidth()][img1.getHeight()];
				int intValues1[][] = new int[img1.getWidth()][img1.getHeight()];
				int intValues2[][] = new int[img1.getWidth()][img1.getHeight()];

				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {

						intValues0[x][y] = originalRaster1.getSample(x, y, 0)
								+ originalRaster2.getSample(x, y, 0);
						intValues1[x][y] = originalRaster1.getSample(x, y, 1)
								+ originalRaster2.getSample(x, y, 1);
						intValues2[x][y] = originalRaster1.getSample(x, y, 2)
								+ originalRaster2.getSample(x, y, 2);
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues0 = DynamicRangeCompressionChannel(intValues0);
					intValues1 = DynamicRangeCompressionChannel(intValues1);
					intValues2 = DynamicRangeCompressionChannel(intValues2);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues0 = LinearRangeCompressionChannel(intValues0);
					intValues1 = LinearRangeCompressionChannel(intValues1);
					intValues2 = LinearRangeCompressionChannel(intValues2);
				}
				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues0[x][y]);
						raster.setSample(x, y, 1, intValues1[x][y]);
						raster.setSample(x, y, 2, intValues2[x][y]);
					}
				}
			} else {
				return img1;
			}
		}
		return ans;

	}

	public static BufferedImage sub(BufferedImage minuend,
			BufferedImage subtrahend, CompressAlgorithm algorithm) {
		BufferedImage ans;
		int color;

		if (minuend == null) {
			return subtrahend;
		} else if (subtrahend == null) {
			return minuend;
		} else if (minuend.getWidth() != subtrahend.getWidth()
				|| minuend.getHeight() != subtrahend.getHeight()) {
			return null;
		} else {
			if (minuend.getType() == BufferedImage.TYPE_BYTE_GRAY
					&& minuend.getType() == BufferedImage.TYPE_BYTE_GRAY) {

				ans = new BufferedImage(minuend.getWidth(),
						minuend.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();
				int intValues[][] = new int[minuend.getWidth()][minuend
						.getHeight()];
				for (int x = 0; x < minuend.getWidth(); x++) {
					for (int y = 0; y < minuend.getHeight(); y++) {
						intValues[x][y] = minuend.getRaster()
								.getSample(x, y, 0)
								- subtrahend.getRaster().getSample(x, y, 0);
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues = DynamicRangeCompressionChannel(intValues);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues = LinearRangeCompressionChannel(intValues);
				}
				for (int x = 0; x < minuend.getWidth(); x++) {
					for (int y = 0; y < minuend.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues[x][y]);
					}
				}
			} else {

				ans = new BufferedImage(minuend.getWidth(),
						minuend.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				WritableRaster originalRaster1 = minuend.getRaster();
				WritableRaster originalRaster2 = subtrahend.getRaster();
				int intValues0[][] = new int[minuend.getWidth()][minuend
						.getHeight()];
				int intValues1[][] = new int[minuend.getWidth()][minuend
						.getHeight()];
				int intValues2[][] = new int[minuend.getWidth()][minuend
						.getHeight()];

				for (int x = 0; x < minuend.getWidth(); x++) {
					for (int y = 0; y < subtrahend.getHeight(); y++) {
						intValues0[x][y] = originalRaster1.getSample(x, y, 0)
								- originalRaster2.getSample(x, y, 0);
						intValues1[x][y] = originalRaster1.getSample(x, y, 1)
								- originalRaster2.getSample(x, y, 1);
						intValues2[x][y] = originalRaster1.getSample(x, y, 2)
								- originalRaster2.getSample(x, y, 2);
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues0 = DynamicRangeCompressionChannel(intValues0);
					intValues1 = DynamicRangeCompressionChannel(intValues1);
					intValues2 = DynamicRangeCompressionChannel(intValues2);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues0 = LinearRangeCompressionChannel(intValues0);
					intValues1 = LinearRangeCompressionChannel(intValues1);
					intValues2 = LinearRangeCompressionChannel(intValues2);
				}
				for (int x = 0; x < minuend.getWidth(); x++) {
					for (int y = 0; y < minuend.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues0[x][y]);
						raster.setSample(x, y, 1, intValues1[x][y]);
						raster.setSample(x, y, 2, intValues2[x][y]);
					}
				}
			}
		}
		return ans;

	}

	public static BufferedImage product(BufferedImage img1, BufferedImage img2,
			CompressAlgorithm algorithm) {
		BufferedImage ans;
		int color;

		if (img1 == null) {
			return img2;
		} else if (img2 == null) {
			return img1;
		} else if (img1.getWidth() != img2.getWidth()
				|| img1.getHeight() != img2.getHeight()) {
			return null;
		} else {
			//
			if (img1.getType() == BufferedImage.TYPE_BYTE_GRAY) {

				ans = new BufferedImage(img1.getWidth(), img1.getHeight(),
						BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();
				int intValues[][] = new int[img1.getWidth()][img1.getHeight()];
				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {
						intValues[x][y] = img1.getRaster().getSample(x, y, 0)
								* img2.getRaster().getSample(x, y, 0);
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues = DynamicRangeCompressionChannel(intValues);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues = LinearRangeCompressionChannel(intValues);
				}
				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues[x][y]);
					}
				}
			} else {
				ans = new BufferedImage(img1.getWidth(), img1.getHeight(),
						BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				WritableRaster originalRaster1 = img1.getRaster();
				WritableRaster originalRaster2 = img2.getRaster();
				int intValues0[][] = new int[img1.getWidth()][img1.getHeight()];
				int intValues1[][] = new int[img1.getWidth()][img1.getHeight()];
				int intValues2[][] = new int[img1.getWidth()][img1.getHeight()];

				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {
						intValues0[x][y] = originalRaster1.getSample(x, y, 0)
								* originalRaster2.getSample(x, y, 0);
						intValues1[x][y] = originalRaster1.getSample(x, y, 1)
								* originalRaster2.getSample(x, y, 1);
						intValues2[x][y] = originalRaster1.getSample(x, y, 2)
								* originalRaster2.getSample(x, y, 2);
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues0 = DynamicRangeCompressionChannel(intValues0);
					intValues1 = DynamicRangeCompressionChannel(intValues1);
					intValues2 = DynamicRangeCompressionChannel(intValues2);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues0 = LinearRangeCompressionChannel(intValues0);
					intValues1 = LinearRangeCompressionChannel(intValues1);
					intValues2 = LinearRangeCompressionChannel(intValues2);
				}
				for (int x = 0; x < img1.getWidth(); x++) {
					for (int y = 0; y < img1.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues0[x][y]);
						raster.setSample(x, y, 1, intValues1[x][y]);
						raster.setSample(x, y, 2, intValues2[x][y]);
					}
				}
			}
			return ans;
		}
	}

	public static BufferedImage scalarProduct(BufferedImage img, float scalar,
			CompressAlgorithm algorithm) {
		BufferedImage ans;
		int color;

		if (img == null) {
			return null;
		} else {
			if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {

				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();
				int intValues[][] = new int[img.getWidth()][img.getHeight()];
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y< img.getHeight(); y++) {
						intValues[x][y] = (int) Math.floor(img.getRaster()
								.getSample(x, y, 0) * scalar);
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues = DynamicRangeCompressionChannel(intValues);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues = LinearRangeCompressionChannel(intValues);
				}
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues[x][y]);
					}
				}
			} else {
				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				WritableRaster originalRaster = img.getRaster();
				int intValues0[][] = new int[img.getWidth()][img.getHeight()];
				int intValues1[][] = new int[img.getWidth()][img.getHeight()];
				int intValues2[][] = new int[img.getWidth()][img.getHeight()];
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						intValues0[x][y] = (int) Math.floor(originalRaster
								.getSample(x, y, 0) * scalar);
						intValues1[x][y] = (int) Math.floor(originalRaster
								.getSample(x, y, 1) * scalar);
						intValues2[x][y] = (int) Math.floor(originalRaster
								.getSample(x, y, 2) * scalar);
					}
				}
				if (algorithm == CompressAlgorithm.DYNAMIC) {
					intValues0 = DynamicRangeCompressionChannel(intValues0);
					intValues1 = DynamicRangeCompressionChannel(intValues1);
					intValues2 = DynamicRangeCompressionChannel(intValues2);
				} else if (algorithm == CompressAlgorithm.LINEAR) {
					intValues0 = LinearRangeCompressionChannel(intValues0);
					intValues1 = LinearRangeCompressionChannel(intValues1);
					intValues2 = LinearRangeCompressionChannel(intValues2);
				}
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						raster.setSample(x, y, 0, intValues0[x][y]);
						raster.setSample(x, y, 1, intValues1[x][y]);
						raster.setSample(x, y, 2, intValues2[x][y]);
					}
				}
			}

			return ans;
		}
	}

	public static BufferedImage negative(BufferedImage img) {
		BufferedImage ans;

		if (img == null) {
			return null;
		} else {
			if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {
				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();

				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {

						raster.setSample(x, y, 0, 255 - img.getRaster()
								.getSample(x, y, 0) - 1);
					}
				}
			} else {
				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_3BYTE_BGR);
				WritableRaster raster = ans.getRaster();
				WritableRaster originalRaster = img.getRaster();
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						raster.setSample(x, y, 0,
								(255 - originalRaster.getSample(x, y, 0)));
						raster.setSample(x, y, 1,
								(255 - originalRaster.getSample(x, y, 1)));
						raster.setSample(x, y, 2,
								(255 - originalRaster.getSample(x, y, 2)));
					}
				}
			}
			return ans;
		}
	}


	/* Umbralizacion */
	public static BufferedImage threshold(BufferedImage img, int thresholdValue) {
		BufferedImage ans;

		if (img == null) {
			return null;
		} else {
			if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {

				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_BYTE_GRAY);
				WritableRaster raster = ans.getRaster();

				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {

						if (img.getRaster().getSample(x, y, 0) <= thresholdValue) {
							raster.setSample(x, y, 0, 0);
						} else {
							raster.setSample(x, y, 0, 255);
						}

					}
				}
			} else {
				ans = new BufferedImage(img.getWidth(), img.getHeight(),
						BufferedImage.TYPE_BYTE_BINARY);
				WritableRaster raster = ans.getRaster();
				for (int x = 0; x < img.getWidth(); x++) {
					for (int y = 0; y < img.getHeight(); y++) {
						Color color = new Color(img.getRGB(x, y));
						// se extraen los valores RGB
						int r = color.getRed();
						int g = color.getGreen();
						int b = color.getBlue();
						// dependiendo del valor del umbral, se van separando
						// los
						// valores RGB a 0 y 255
						r = (r > thresholdValue) ? 255 : 0;
						g = (g > thresholdValue) ? 255 : 0;
						b = (b > thresholdValue) ? 255 : 0;
						ans.setRGB(x, y, new Color(r, g, b).getRGB());

					}
				}
				BufferedImage gray = new BufferedImage(ans.getWidth(),
						ans.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
				ColorConvertOp op = new ColorConvertOp(ans.getColorModel()
						.getColorSpace(), gray.getColorModel().getColorSpace(),
						null);
				op.filter(ans, gray);
				ans = gray;
			}
			return ans;
		}
	}

	private static int[] maxPixel(BufferedImage img) {
		int resp[] = new int[3];
		int max0 = Integer.MIN_VALUE;
		int max2 = Integer.MIN_VALUE;
		int max1 = Integer.MIN_VALUE;

		for (int x = 0; x < img.getWidth(); x++) {
			for (int y = 0; y < img.getHeight(); y++) {
				if (img.getRaster().getSample(x, y, 0) > max0) {
					max0 = img.getRaster().getSample(x, y, 0);
				}
				if (img.getType() == BufferedImage.TYPE_3BYTE_BGR) {
					if (img.getRaster().getSample(x, y, 1) > max1) {
						max1 = img.getRaster().getSample(x, y, 1);
					}
					if (img.getRaster().getSample(x, y, 2) > max2) {
						max2 = img.getRaster().getSample(x, y, 2);
					}
				}
			}
		}
		resp[0] = max0;
		resp[1] = max1;
		resp[2] = max2;
		return resp;
	}

	/**
	 * Esto agarra un canal y lo mapea al 0-255 con el algoritmo de dynamic
	 * range compresion
	 * 
	 * @param colors
	 * @return
	 */
	public static int[][] DynamicRangeCompressionChannel(int[][] colors) {
		int max0 = Integer.MIN_VALUE;
		for (int x = 0; x < colors.length; x++) {
			for (int y = 0; y < colors[x].length; y++) {
				if (colors[x][y] > max0) {
					max0 = colors[x][y];
				}
			}
		}
		double c1 = ((double) 255 / Math.log(1 + max0));
		int ans[][] = new int[colors.length][colors[0].length];
		for (int x = 0; x < colors.length; x++) {
			for (int y = 0; y < colors[x].length; y++) {
				double sample = c1 * Math.log(1 + colors[x][y]);
				if (Math.floor(sample) > 255 || Math.floor(sample) < 0) {
					System.err.println("Huston tenemos un problema");
				}
				ans[x][y] = (int) Math.floor(sample);
			}
		}
		return ans;
	}
	public static BufferedImage LinearRangeCompressionChannel(int[][] colors,BufferedImage src) {
		int max0 = Integer.MIN_VALUE;
		int min0 = Integer.MAX_VALUE;
		for (int x = 0; x < colors.length; x++) {
			for (int y = 0; y < colors[x].length; y++) {
				if (colors[x][y] > max0) {
					max0 = colors[x][y];
				}
				if (colors[x][y] < min0) {
					min0 = colors[x][y];
				}
			}
		}
		double c1 = ((double) 255 / (max0 - min0));
		for (int x = 0; x < colors.length; x++) {
			for (int y = 0; y < colors[x].length; y++) {
				double sample = c1 * (colors[x][y] - min0);
				// System.out.println("pixel:" + Math.floor(sample));
				src.getRaster().setSample(x,y,0, (int) Math.floor(sample));
			}
		}
		return src;

	}
	

	/**
	 * Esto mapea del minPixel a MaxPixel al 0 - 255;
	 * 
	 * @param colors
	 * @return
	 */
	public static int[][] LinearRangeCompressionChannel(int[][] colors) {
		int max0 = Integer.MIN_VALUE;
		int min0 = Integer.MAX_VALUE;
		System.out.println("****" + colors.length);
		System.out.println("****" + colors[0].length);
		for (int x = 0; x < colors.length; x++) {
			for (int y = 0; y < colors[x].length; y++) {
				if (colors[x][y] > max0) {
					max0 = colors[x][y];
				}
				if (colors[x][y] < min0) {
					min0 = colors[x][y];
				}
			}
		}
		double c1 = ((double) 255 / (max0 - min0));
		int ans[][] = new int[colors.length][colors[0].length];
		for (int x = 0; x < colors.length; x++) {
			for (int y = 0; y < colors[x].length; y++) {
				double sample = c1 * (colors[x][y] - min0);
				// System.out.println("pixel:" + Math.floor(sample));
				ans[x][y] = (int) Math.floor(sample);
			}
		}
		return ans;

	}
	
	public static BufferedImage generateCompressedImage1Channel(int[][] intValues, CompressAlgorithm algorithm){
		
		BufferedImage img = new BufferedImage(intValues.length, intValues[0].length, BufferedImage.TYPE_BYTE_GRAY);
		
		if (algorithm == CompressAlgorithm.DYNAMIC) {
			intValues = DynamicRangeCompressionChannel(intValues);
		} else if (algorithm == CompressAlgorithm.LINEAR) {
			intValues = LinearRangeCompressionChannel(intValues);
		}
		WritableRaster raster = img.getRaster();
		for (int x = 0; x < img.getWidth(); x++) {
			for (int y = 0; y < img.getHeight(); y++) {
				raster.setSample(x, y, 0, intValues[x][y]);
			}
		}
		return img;
		
	}
	
	public static BufferedImage generateCompressedImage3Channel(int[][] intValues0,int[][] intValues1,int[][] intValues2, CompressAlgorithm algorithm){
		
		BufferedImage img = new BufferedImage(intValues0.length, intValues0[0].length, BufferedImage.TYPE_3BYTE_BGR);
		
		if (algorithm == CompressAlgorithm.DYNAMIC) {
			intValues0 = DynamicRangeCompressionChannel(intValues0);
			intValues1 = DynamicRangeCompressionChannel(intValues1);
			intValues2 = DynamicRangeCompressionChannel(intValues2);
		} else if (algorithm == CompressAlgorithm.LINEAR) {
			intValues0 = LinearRangeCompressionChannel(intValues0);
			intValues1 = LinearRangeCompressionChannel(intValues1);
			intValues2 = LinearRangeCompressionChannel(intValues2);
		}
		WritableRaster raster = img.getRaster();
		for (int x = 0; x < img.getWidth(); x++) {
			for (int y = 0; y < img.getHeight(); y++) {
				raster.setSample(x, y, 0, intValues0[x][y]);
				raster.setSample(x, y, 1, intValues1[x][y]);
				raster.setSample(x, y, 2, intValues2[x][y]);
			}
		}
		return img;
		
	}

	public static BufferedImage DynamicRangeCompression(BufferedImage img) {
		double c1, c2, c3;
		BufferedImage ans = null;

		// c= 255/Math.log10(1 + 2);
		int max0 = Integer.MIN_VALUE;
		int max2 = Integer.MIN_VALUE;
		int max1 = Integer.MIN_VALUE;
		int maxPixels[] = maxPixel(img);
		max0 = maxPixels[0];

		if (img.getType() == BufferedImage.TYPE_3BYTE_BGR) {
			max1 = maxPixels[1];
			max2 = maxPixels[2];
		}
		if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {
			c1 = ((double) 255 / Math.log(1 + max0));
			ans = new BufferedImage(img.getWidth(), img.getHeight(),
					BufferedImage.TYPE_BYTE_GRAY);
			WritableRaster raster = ans.getRaster();
			for (int x = 0; x < img.getWidth(); x++) {
				for (int y = 0; y < img.getHeight(); y++) {
					double sample = c1
							* Math.log(1 + img.getRaster().getSample(x, y, 0));
					raster.setSample(x, y, 0, sample);
				}
			}
		} else if (img.getType() == BufferedImage.TYPE_3BYTE_BGR) {
			c1 = ((double) 255 / Math.log(1 + max0));
			c2 = ((double) 255 / Math.log(1 + max1));
			c3 = ((double) 255 / Math.log(1 + max2));
			ans = new BufferedImage(img.getWidth(), img.getHeight(),
					BufferedImage.TYPE_3BYTE_BGR);
			WritableRaster raster = ans.getRaster();
			for (int x = 0; x < img.getWidth(); x++) {
				for (int y = 0; y < img.getHeight(); y++) {
					double sample0 = c1
							* Math.log(1 + img.getRaster().getSample(x, y, 0));
					raster.setSample(x, y, 0, sample0);
					double sample1 = c2
							* Math.log(1 + img.getRaster().getSample(x, y, 1));
					raster.setSample(x, y, 1, sample1);
					double sample2 = c3
							* Math.log(1 + img.getRaster().getSample(x, y, 2));
					raster.setSample(x, y, 2, sample2);
				}
			}
		}
		return ans;
	}

	public static BufferedImage enhanceContrast(BufferedImage image) {
		BufferedImage ret;
		Histogram h = new Histogram(image);

		double[] eqlevels = h.getEqualizedLevels();

		// TODO agregar para RGB
		ret = new BufferedImage(image.getWidth(), image.getHeight(),
				BufferedImage.TYPE_BYTE_GRAY);
		WritableRaster wr = ret.getRaster();
		Raster r = image.getRaster();

		for (int i = 0; i < image.getHeight(); i++) {
			for (int j = 0; j < image.getWidth(); j++) {
				wr.setSample(i, j, 0, eqlevels[r.getSample(i, j, 0)]);
			}
		}
		return ret;
	}
	
	// converts imgData as array to Matrix(nxm). n*m == imgData.length must be met
	public static Double[][] toMatrix(Double[] imgData, int rows, int cols) throws IllegalArgumentException {
		Double[][] imgMatrix;
		if (rows*cols != imgData.length) {
			throw new IllegalArgumentException("Dimensions do not match");
		}
		
		imgMatrix = new Double[rows][cols];
		
		for (int y = 0 ; y < rows ; y++) {
			for (int x = 0 ; x < cols ; x++) {
				imgMatrix[y][x] = imgData[y*cols + x];
			}
		}
		
		return imgMatrix;
	}
}
